package com.eastfair.questionnaire.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.eastfair.boot.service.SuperServiceImpl;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.utils.ConvertUtil;
import com.eastfair.core.utils.StringUtils;
import com.eastfair.core.utils.ValidateUtil;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;
import com.eastfair.core.snowflake.DefaultUidGenerator;
import com.eastfair.questionnaire.dao.AnswerMapper;
import com.eastfair.questionnaire.dto.AnswerDTO;
import com.eastfair.questionnaire.dto.QuestionDTO;
import com.eastfair.questionnaire.entity.Answer;
import com.eastfair.questionnaire.entity.AnswerResult;
import com.eastfair.questionnaire.service.AnswerResultService;
import com.eastfair.questionnaire.service.AnswerService;
import com.eastfair.questionnaire.vo.AnswerVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 答案
 * </p>
 *
 * @author yuwx
 * @date 2021-08-18
 */
@Slf4j
@Service
public class AnswerServiceImpl extends SuperServiceImpl<AnswerMapper, Answer> implements AnswerService {
    private AnswerResultService answerResultService;
    private ProjectCoreServiceFeign projectCoreServiceFeign;

    @Autowired
    public AnswerServiceImpl(AnswerResultService answerResultService, ProjectCoreServiceFeign projectCoreServiceFeign) {
        this.answerResultService = answerResultService;
        this.projectCoreServiceFeign = projectCoreServiceFeign;
    }

    /**************sharding 分库分表操作 *****************/
    @Override
    public boolean updateByIdOfShard(Answer entity) {
        UpdateWrapper<Answer> updateWrapper = new UpdateWrapper<>();
        //可将指定字段更新为null
        updateWrapper.lambda().eq(Answer::getProjectId, entity.getProjectId()).eq(Answer::getId, entity.getId());
        entity.setProjectId(null);
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    /**
     * 处理新增相关处理
     *
     * @param model 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerSave(Answer model) {
        //雪花ID
        if (Objects.isNull(model.getId())) {
            model.setId(DefaultUidGenerator.getUidOfProjectId(ContextUtil.getProjectId()));
        }
        model.setProjectId(ContextUtil.getProjectId());
        return R.successDef();
    }

    @Override
    public List<AnswerVO> list(Long projectId, Long questionId) {
        if (Objects.isNull(projectId)
                || Objects.isNull(questionId)) {
            return Collections.emptyList();
        }
        QueryWrapper<Answer> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Answer::getProjectId, projectId)
                .eq(Answer::getQuestionId, questionId)
                .eq(Answer::getIsEnable, BusinessConstant.ENABLE_YES)
                .eq(Answer::getIsDelete, BusinessConstant.NO)
                .orderByAsc(Answer::getSequence);
        List<Answer> answerList = list(queryWrapper);
        if (CollectionUtil.isEmpty(answerList)) {
            return Collections.emptyList();
        }
        return ConvertUtil.convertList(answerList, AnswerVO.class);
    }

    @Override
    public List<AnswerVO> list(Long projectId, List<Long> answerIdList) {
        if (Objects.isNull(projectId)) {
            throw new NullPointerException("params is null");
        }
        if (CollectionUtil.isEmpty(answerIdList)) {
            return Collections.emptyList();
        }
        QueryWrapper<Answer> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(Answer::getProjectId, projectId)
                .in(Answer::getId, answerIdList);
        List<Answer> answerList = list(queryWrapper);
        if (CollectionUtil.isEmpty(answerList)) {
            return Collections.emptyList();
        }
        return ConvertUtil.convertList(answerList, AnswerVO.class);
    }

    @Override
    public List<AnswerVO> listByLevel(Long projectId, Long questionId) {
        if (Objects.isNull(projectId)
                || Objects.isNull(questionId)) {
            throw new NullPointerException("params is null");
        }
        List<AnswerVO> answerList = list(projectId, questionId);
        List<AnswerVO> answerLevelList = assembleAnswerLeve(answerList);
        return answerLevelList;
    }

    @Override
    public List<AnswerVO> listByLevel(Long projectId, Long questionId, String subjectType, Long subjectId) {
        if (Objects.isNull(projectId)) {
            throw new NullPointerException("params is null");
        }
        if (Objects.isNull(questionId)) {
            return Collections.emptyList();
        }

        List<AnswerVO> answerList = list(projectId, questionId);
        if (!StringUtils.isBlank(subjectType) && !Objects.isNull(subjectId)) {
            List<AnswerResult> answerResultList = answerResultService.listByQuestion(projectId, questionId, subjectType, subjectId);
            setAnswerResult(answerList, answerResultList);
        }
        List<AnswerVO> answerLevelList = assembleAnswerLeve(answerList);
        return answerLevelList;
    }

    @Override
    public List<AnswerVO> listSelected(Long projectId, List<Long> questionIdList, String subjectType, Long subjectId) {
        if (Objects.isNull(projectId) || Objects.isNull(subjectId) || StringUtils.isBlank(subjectType)) {
            throw new NullPointerException("params is null");
        }
        if (CollectionUtil.isEmpty(questionIdList)) {
            return Collections.emptyList();
        }
        List<AnswerResult> answerResultList = answerResultService.listByQuestion(projectId, questionIdList, subjectType, subjectId);
        if (CollectionUtil.isEmpty(answerResultList)) {
            return Collections.emptyList();
        }
        List<Long> answerIdList = answerResultList.stream().map(AnswerResult::getAnswerId).collect(Collectors.toList());
        List<AnswerVO> answerList = list(projectId, answerIdList);
        return answerList;
    }

    @Override
    public Map<Long, List<AnswerVO>> groupByQuestionId(Long projectId, List<Long> answerIdList) {
        List<AnswerVO> answerList = list(projectId, answerIdList);
        Map<Long, List<AnswerVO>> groupMap = new HashMap<>();
        answerList.stream().forEach(answer -> {
            List<AnswerVO> questionMapValue = groupMap.getOrDefault(answer.getQuestionId(), new ArrayList<>());
            questionMapValue.add(answer);
            groupMap.put(answer.getQuestionId(), questionMapValue);
        });
        return groupMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AnswerVO save(QuestionDTO question, AnswerDTO answerDTO) {
        if (Objects.isNull(answerDTO) || Objects.isNull(question)) {
            throw new NullPointerException("params is null");
        }
        if (!ValidateUtil.validateSimple(answerDTO, AnswerDTO.validateSave.class)) {
            throw new IllegalArgumentException("params is error");
        }
        if (!Objects.isNull(answerDTO.getParentAnswerId())) {
            updateAsParent(answerDTO.getProjectId(), answerDTO.getParentAnswerId());
        }
        if (Objects.isNull(answerDTO.getNextQuestionId())) {
            answerDTO.setNextQuestionId(question.getNextQuestionId());
        }
        Answer answer = BeanUtil.toBean(answerDTO, Answer.class);
        answer.setId(DefaultUidGenerator.getUidOfProjectId(answerDTO.getProjectId()));
        save(answer);
        return BeanUtil.toBean(answer, AnswerVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long projectId, Long answerId) {
        Answer answer = Answer.builder().id(answerId).projectId(projectId).isDelete(BusinessConstant.YES).build();
        updateByIdOfShard(answer);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByQuestion(Long projectId, Long questionId) {
        UpdateWrapper<Answer> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(Answer::getProjectId, projectId).eq(Answer::getQuestionId, questionId);
        Answer answer = Answer.builder().isDelete(BusinessConstant.YES).build();
        SqlHelper.retBool(this.getBaseMapper().update(answer, updateWrapper));
    }

    /**
     * Description 判断是否是叶子节点
     *
     * @param answerVO
     * @return boolean
     * @Date 15:32 2021/8/25
     * @Author yuwx
     **/
    @Override
    public boolean isLeafAnswer(AnswerVO answerVO) {
        if (!Objects.isNull(answerVO.getIsLeaf()) && (BusinessConstant.YES == answerVO.getIsLeaf())) {
            return true;
        }
        return false;
    }


    /**
     * Description 判断是否是顶级节点
     *
     * @param answerVO
     * @return boolean
     * @Date 15:33 2021/8/25
     * @Author yuwx
     **/
    @Override
    public boolean isTopAnswer(AnswerVO answerVO) {
        if (Objects.isNull(answerVO.getParentAnswerId())) {
            return true;
        }
        return false;
    }

    /**
     * Description 同一问题下的答案组装为树状
     *
     * @param answerList
     * @return java.util.List<com.eastfair.questionnaire.vo.AnswerVO>
     * @Date 15:33 2021/8/25
     * @Author yuwx
     **/
    private List<AnswerVO> assembleAnswerLeve(List<AnswerVO> answerList) {
        final Map<Long, AnswerVO> answerMap = new HashMap<>(answerList.size());
        final List<AnswerVO> answerLevelList = new ArrayList<>();
        final List<AnswerVO> answerNotFixedList = new ArrayList<>();
        answerList.stream().forEach(answerVO -> {
            if (!isLeafAnswer(answerVO)) {
                answerMap.put(answerVO.getId(), answerVO);
            }
            if (isTopAnswer(answerVO)) {
                answerLevelList.add(answerVO);
                return;
            }
            AnswerVO parentAnswer = answerMap.get(answerVO.getParentAnswerId());
            if (Objects.isNull(parentAnswer)) {
                answerNotFixedList.add(answerVO);
                return;
            }
            addSubAnswer(parentAnswer, answerVO);
        });

        while (!answerNotFixedList.isEmpty()) {
            for (int i = 0; i < answerNotFixedList.size(); i++) {
                AnswerVO answerVO = answerNotFixedList.get(i);
                AnswerVO parentAnswer = answerMap.get(answerVO.getParentAnswerId());
                if (Objects.isNull(parentAnswer)) {
                    continue;
                }
                addSubAnswer(parentAnswer, answerVO);
                answerNotFixedList.remove(i--);
            }
        }
        return answerLevelList;
    }

    /**
     * Description 将用户同一问题下答案结果填充到答案上
     *
     * @param answerList
     * @param answerResultList
     * @return void
     * @Date 15:34 2021/8/25
     * @Author yuwx
     **/
    private void setAnswerResult(List<AnswerVO> answerList, List<AnswerResult> answerResultList) {
        final Map<Long, AnswerVO> leafAnswerMap = answerList.stream().collect(Collectors.toMap(AnswerVO::getId, AnswerVO -> AnswerVO));
        answerResultList.stream().forEach(answerResult -> {
            AnswerVO answer = leafAnswerMap.get(answerResult.getAnswerId());
            if (Objects.isNull(answer)) {
                return;
            }
            answer.setIsSelected(BusinessConstant.YES);
            answer.setInputResult(answerResult.getInputResult());
        });
    }

    /**
     * Description 向父答案添加子答案
     *
     * @param parentAnswer
     * @param subAnswer
     * @return void
     * @Date 15:35 2021/8/25
     * @Author yuwx
     **/
    private void addSubAnswer(AnswerVO parentAnswer, AnswerVO subAnswer) {
        List<AnswerVO> subAnswerList = parentAnswer.getSubAnswerList();
        if (CollectionUtil.isEmpty(subAnswerList)) {
            subAnswerList = new ArrayList<>();
            parentAnswer.setSubAnswerList(subAnswerList);
        }
        subAnswerList.add(subAnswer);
    }

    /**
     * Description 升级为父节点
     *
     * @param projectId
     * @param answerId
     * @return void
     * @Date 11:18 2021/9/1
     * @Author yuwx
     **/
    private void updateAsParent(Long projectId, Long answerId) {
        if (Objects.isNull(projectId) || Objects.isNull(answerId)) {
            throw new NullPointerException("params is null");
        }
        Answer answer = Answer.builder().isLeaf(BusinessConstant.NO).projectId(projectId).id(answerId).build();
        updateByIdOfShard(answer);
    }
}
